package com.stottlerhenke.simbionic.editor;

import java.util.Stack;

import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;

import org.xml.sax.Attributes;
import org.xml.sax.Locator;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;

import com.stottlerhenke.simbionic.editor.gui.SB_BindingsHolder;
import com.stottlerhenke.simbionic.editor.gui.SB_Catalog;
import com.stottlerhenke.simbionic.editor.gui.SB_Category;
import com.stottlerhenke.simbionic.editor.gui.SB_Condition;
import com.stottlerhenke.simbionic.editor.gui.SB_Connector;
import com.stottlerhenke.simbionic.editor.gui.SB_ConnectorComposite;
import com.stottlerhenke.simbionic.editor.gui.SB_Descriptor;
import com.stottlerhenke.simbionic.editor.gui.SB_Descriptors;
import com.stottlerhenke.simbionic.editor.gui.SB_Element;
import com.stottlerhenke.simbionic.editor.gui.SB_ElementComposite;
import com.stottlerhenke.simbionic.editor.gui.SB_Polymorphism;
import com.stottlerhenke.simbionic.editor.gui.SB_Rectangle;

/**
 * Import an XML behavior model generated by the C++ editor
 *
 */
public class SimBionicXMLHandler extends DefaultHandler  
{
	protected SimBionicEditor mEditorApp;
	protected SB_Catalog mCatalog;
	protected SB_Descriptors _descriptors;
    protected int _xmlVersion;
	
	/**
	 *  
	 */
	public SimBionicXMLHandler(SimBionicEditor pEditorApp)
	{
		mEditorApp = pEditorApp;
		mCatalog = pEditorApp.getCatalog();
        _descriptors = pEditorApp.getProjectBar().getDescriptors();
        _xmlVersion = 0;
	}

  protected Stack _qNames;

  protected String _text;

  protected String _main;

  protected int _initialId;

  protected int _cx, _cy;

  protected int _startId, _endId;

  protected int _x1, _y1, _x2, _y2;

  ////////////////////////////////////////////////////////////////////
  // ContentHandler events.
  ////////////////////////////////////////////////////////////////////
  public void startDocument() {
      mCatalog.clearCatalog();
      _descriptors.clearDescriptors();
      //setSelectedIndex(0);

      _qNames = new Stack();
  }

  public void endDocument() {
      ((DefaultTreeModel) mCatalog.getModel()).reload();
      ((DefaultTreeModel) _descriptors.getModel()).reload();
      _descriptors.expandAll();

      _qNames = null;
      _text = null;
      _main = null;
  }

  public void startElement(String uri, String name, String qName,
          Attributes atts) {
      _text = null;
      
      if (name.trim().length() == 0)
          // for some reason name is always an empty string now (presumably it wasn't
          // at some point because this code worked!)
          name = qName; 

      if (name.equals("actions"))
          _qNames.push(mCatalog._actions);
      else if (name.equals("predicates"))
          _qNames.push(mCatalog._predicates);
      else if (name.equals("behaviors"))
          _qNames.push(mCatalog._behaviors);
      else if (name.equals("globals"))
          _qNames.push(mCatalog._globals);
      else if (name.equals("constants"))
          _qNames.push(mCatalog._constants);
      else if (name.equals("locals")) {
          SB_Polymorphism poly = (SB_Polymorphism) _qNames.peek();
          _qNames.push(poly._locals);
      } else if (name.equals("nodes") || name.equals("conditions")) {
          SB_Polymorphism poly = (SB_Polymorphism) _qNames.peek();
          _qNames.push(poly._elements);
      } else if (name.equals("connectors")) {
          SB_Polymorphism poly = (SB_Polymorphism) _qNames.peek();
          _qNames.push(poly._connectors);
      } else if (name.equals("descriptors"))
          _qNames.push(_descriptors.getRoot());
      else if (name.equals("action")) {
          DefaultMutableTreeNode treeNode = new DefaultMutableTreeNode(
                  new SB_Action());
          ((DefaultMutableTreeNode) _qNames.peek()).add(treeNode);
          _qNames.push(treeNode);
      } else if (name.equals("predicate")) {
          DefaultMutableTreeNode treeNode = new DefaultMutableTreeNode(
                  new SB_Predicate());
          ((DefaultMutableTreeNode) _qNames.peek()).add(treeNode);
          _qNames.push(treeNode);
      } else if (name.equals("behavior") && _qNames.get(1).equals("sbp")) {
          SB_Behavior behavior = new SB_Behavior();
          behavior.clearPolys();
          behavior.setEditor(mEditorApp);
          DefaultMutableTreeNode treeNode = new DefaultMutableTreeNode(
                  behavior);
          ((DefaultMutableTreeNode) _qNames.peek()).add(treeNode);
          _qNames.push(treeNode);
      } else if (name.equals("poly")) {
          int size = _qNames.size();
          DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) _qNames
                  .get(size - 2);
          SB_Behavior behavior = (SB_Behavior) treeNode.getUserObject();
          SB_Polymorphism poly = new SB_Polymorphism(behavior);
          poly._indices.removeAllElements();
          behavior.addPoly(poly);
          _qNames.push(poly);
      } else if (name.equals("folder")) {
          DefaultMutableTreeNode treeNode = new DefaultMutableTreeNode(
                  new SB_Folder());
          ((DefaultMutableTreeNode) _qNames.peek()).add(treeNode);
          _qNames.push(treeNode);
      } else if (name.equals("param")) {
          DefaultMutableTreeNode treeNode = new DefaultMutableTreeNode(
                  new SB_Parameter());
          ((DefaultMutableTreeNode) _qNames.peek()).add(treeNode);
          _qNames.push(treeNode);
      } else if (name.equals("global")) {
          DefaultMutableTreeNode treeNode = new DefaultMutableTreeNode(
                  new SB_Global(""));
          mCatalog._globals.add(treeNode);
          _qNames.push(treeNode);
      } else if (name.equals("constant")) {
          DefaultMutableTreeNode treeNode = new DefaultMutableTreeNode(
                  new SB_Constant(""));
          mCatalog._constants.add(treeNode);
          _qNames.push(treeNode);
      } else if (name.equals("local")) {
          DefaultMutableTreeNode treeNode = new DefaultMutableTreeNode(
                  new SB_Variable());
          ((DefaultMutableTreeNode) _qNames.peek()).add(treeNode);
          _qNames.push(treeNode);
      } else if (name.equals("node")) {
          SB_Rectangle node = new SB_Rectangle();
          ((SB_ElementComposite) _qNames.peek())._drawables.add(node);
          _qNames.push(node);
      } else if (name.equals("condition")) {
          SB_Condition condition = new SB_Condition();
          ((SB_ElementComposite) _qNames.peek())._drawables
                  .add(condition);
          _qNames.push(condition);
      } else if (name.equals("connector")) {
          SB_Connector connector = new SB_Connector();
          if (_startId != -1)
              connector._startElement = (SB_Element) _qNames.peek();
          else
              connector.p1.setLocation(_x1, _y1);
          int size = _qNames.size();
          ((SB_ConnectorComposite) _qNames.get(size - 2))._drawables
                  .add(connector);
          _qNames.push(connector);
      } else if (name.equals("binding")) {
          _qNames.push(new SB_Binding("", ""));
      } else if (name.equals("category")) {
          DefaultMutableTreeNode treeNode = new DefaultMutableTreeNode(new SB_Category());
          ((DefaultMutableTreeNode) _qNames.peek()).add(treeNode);
          _qNames.push(treeNode);
      } else if (name.equals("descriptor")) {
          DefaultMutableTreeNode treeNode = new DefaultMutableTreeNode(new SB_Descriptor());
          ((DefaultMutableTreeNode) _qNames.peek()).add(treeNode);
          _qNames.push(treeNode);
      } else
          _qNames.push(name);
  }

  public void endElement(String uri, String name, String qName) {
      if (_text != null)
          processText();

      if (name.trim().length() == 0)
          // for some reason name is always an empty string now (presumably it wasn't
          // at some point because this code worked!)
          name = qName; 
      
      if (name.equals("binding")) {
          int size = _qNames.size();
          ((SB_BindingsHolder) _qNames.get(size - 3))
                  .addBinding((SB_Binding) _qNames.peek());
      } else if (name.equals("connectors")) {
          SB_ConnectorComposite connectors = (SB_ConnectorComposite) _qNames.peek();
          connectors.updateTwoWay();
      } 
      
      _qNames.pop();
  }

  public void characters(char ch[], int start, int length) {
      if (_qNames.empty())
          return;
      if (!(_qNames.peek() instanceof String))
          return;

      String text = new String(ch, start, length);
      if (_text == null)
          _text = text;
      else
          _text += text;
  }

  protected void processText() {
      if (_qNames.empty())
          return;
      if (!(_qNames.peek() instanceof String))
          return;

      String qName = (String) _qNames.peek();
      String text = _text;

      int size = _qNames.size();
      if (size >= 2
              && _qNames.get(size - 2) instanceof DefaultMutableTreeNode) {
          DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) _qNames
                  .get(size - 2);
          UserObject userObject = (UserObject) treeNode.getUserObject();
          if (qName.equals("name")) {
              userObject.setName(text);
              if (userObject instanceof SB_Category) {
                  SB_Global global = new SB_Global("g" + text);
                  global.setType(SB_Variable.kString);
                  global.setPolymorphic(true);
                  treeNode = new DefaultMutableTreeNode(global);
                  mCatalog._globals.add(treeNode);
              }
          } else if (userObject instanceof SB_Function) {
              SB_Function function = (SB_Function) userObject;
              if (qName.equals("id"))
                  function.setId(Integer.parseInt(text));
              else if (qName.equals("descr"))
                  function.setDescription(text);
              else if (qName.equals("core"))
                  function.setCore(Integer.parseInt(text) != 0);
              else if (qName.equals("ret"))
                  ((SB_Predicate) function).setRetType(Integer
                          .parseInt(text));
              else if (qName.equals("exec"))
                  ((SB_Behavior) function)
                          .setExec(Integer.parseInt(text));
              else if (qName.equals("interrupt"))
                  ((SB_Behavior) function).setInterrupt(Integer
                          .parseInt(text));
          } else if (userObject instanceof SB_Variable) {
              SB_Variable var = (SB_Variable) userObject;
              if (qName.equals("type"))
                  var.setType(Integer.parseInt(text));
              else if (qName.equals("dir"))
                  ((SB_Parameter) var).setDirection(Integer
                          .parseInt(text));
              else if (qName.equals("initial"))
                  ((SB_Global) var).setInitial(((SB_Global) var)
                          .getInitial()
                          + text);
              else if (qName.equals("value"))
                  ((SB_Constant) var).setValue(((SB_Constant) var)
                          .getValue()
                          + text);
          }
      } else if (qName.equals("name")) {
          if (_qNames.get(size - 2).equals("behavior")) {
              DefaultMutableTreeNode treeNode = mCatalog
                      .findFuncNode(text);
              _qNames.set(size - 2, treeNode);
              if (text.equals(_main)) {
                  SB_Behavior behavior = (SB_Behavior) treeNode
                          .getUserObject();
                  mCatalog._main = behavior;
                  behavior.setMain(true);
              }
          }
      } else if (qName.equals("main")) {
          _main = text;
      } else if (qName.equals("index")) {
          SB_Polymorphism poly = (SB_Polymorphism) _qNames.get(size - 3);
          poly._indices.add(text);
      } else if (qName.equals("initial")) {
          _initialId = Integer.parseInt(text);
      } else if (size >= 2 && _qNames.get(size - 2) instanceof SB_Element) {
          SB_Element element = (SB_Element) _qNames.get(size - 2);
          if (qName.equals("id")) {
              int id = Integer.parseInt(text);
              element.setId(id);
              if (element instanceof SB_Rectangle && id == _initialId)
                  ((SB_Rectangle) element)._initial = true;
          } else if (qName.equals("expr"))
              element._expr = text;
          else if (qName.equals("cx"))
              _cx = Integer.parseInt(text);
          else if (qName.equals("cy")) {
              _cy = Integer.parseInt(text);
              element.setCenter(_cx, _cy);
          } else if (qName.equals("final"))
              ((SB_Rectangle) element)._final = Integer.parseInt(text) != 0;
      } else if (size >= 2 && _qNames.get(size - 2).equals("start")) {
          if (qName.equals("id"))
              _startId = Integer.parseInt(text);
          else if (qName.equals("type")) {
              int startType = Integer.parseInt(text);
              SB_Polymorphism poly = (SB_Polymorphism) _qNames
                      .get(size - 4);
              SB_Element startElement = poly._elements.findById(_startId,
                      startType);
              _qNames.set(size - 2, startElement);
          } else if (qName.equals("x"))
              _x1 = Integer.parseInt(text);
          else if (qName.equals("y"))
              _y1 = Integer.parseInt(text);
      } else if (size >= 2
              && _qNames.get(size - 2) instanceof SB_Connector) {
          SB_Connector connector = (SB_Connector) _qNames.get(size - 2);
          if (qName.equals("id"))
              connector.setId(Integer.parseInt(text));
          else if (qName.equals("priority")) {
              int priority = Integer.parseInt(text);
              connector.setPriority(priority);
              if (priority != -1) {
                  SB_Element startElement = (SB_Element) _qNames
                          .get(size - 3);
                  if (startElement._connectors.size() < priority)
                      startElement._connectors.setSize(priority);
                  startElement._connectors.set(priority - 1, connector);
              }
          } else if (qName.equals("interrupt"))
              connector._interrupt = Integer.parseInt(text) != 0;
      } else if (size >= 2 && _qNames.get(size - 2).equals("end")) {
          if (qName.equals("id"))
              _endId = Integer.parseInt(text);
          else if (qName.equals("type")) {
              int endType = Integer.parseInt(text);
              SB_Polymorphism poly = (SB_Polymorphism) _qNames
                      .get(size - 6);
              SB_Element endElement = poly._elements.findById(_endId,
                      endType);
              SB_Connector connector = (SB_Connector) _qNames
                      .get(size - 3);
              connector._endElement = endElement;
          } else if (qName.equals("x"))
              _x2 = Integer.parseInt(text);
          else if (qName.equals("y")) {
              _y2 = Integer.parseInt(text);
              SB_Connector connector = (SB_Connector) _qNames
                      .get(size - 3);
              connector.p2.setLocation(_x2, _y2);
          }
      } else if (size >= 2 && _qNames.get(size - 2) instanceof SB_Binding) {
          SB_Binding binding = (SB_Binding) _qNames.get(size - 2);
          if (qName.equals("var"))
              binding.setVar(text);
          else if (qName.equals("expr"))
              binding.setExpr(text);
      } else if (qName.equals("sim"))
          //_fileSIM = new File(text);
      	FileManager.getInstance().setSimFile(text);
      _text = null;
  }

  public void skippedEntity(String name) {
  }

  public void processingInstruction(String target, String data) {
  }

  public void ignorableWhitespace(char[] ch, int start, int length) {
  }

  public void startPrefixMapping(String prefix, String uri) {
  }

  public void endPrefixMapping(String prefix) {
  }

  public void setDocumentLocator(Locator locator) {
  }
  
  protected int parseType(String typeStr)
  {
      if (_xmlVersion < 10)
      {
          // old type constants
          return Integer.parseInt(typeStr);
      }
      else
      {
          // new type constants (matching the engine)
          int type = Integer.parseInt(typeStr);
          switch (type)
          {
          case 5 : return 7;    // any
          case 6 : return 5;    // integer
          case 7 : return 6;    // boolean
          default: return type; // others are unchanged
          }
      }
  }

  ////////////////////////////////////////////////////////////////////
  // ErrorHandler events.
  ////////////////////////////////////////////////////////////////////

  public void error(SAXParseException exception) {
      System.err.println("SAXParseException: error");
  }

  public void fatalError(SAXParseException exception) {
      System.err.println("SAXParseException: fatalError");
  }

  public void warning(SAXParseException exception) {
      System.err.println("SAXParseException: warning");
  }
}
